//
//  NSObject+ThreadComm.m
//  Networking
//
//  Created by Christoph Eßer on 12/7/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//
// Special thanks to Excitabyte:
// http://excitabyte.wordpress.com/2010/05/18/part-ii-spawning-threads-using-selectors-with-multiple-parameters/


#import "NSObject+ThreadComm.h"


@implementation NSObject (NSObject_ThreadComm)
-(void) performSelector: (SEL) aSelector onThread: (NSThread*) aThread waitUntilDone: (BOOL) wait withObjects: (void*) objects,... {
    // We'll need an NSInvocation, since it lets us define arguments
    // for multiple parameters.
    NSMethodSignature *theSignature = [self methodSignatureForSelector:aSelector];
    NSInvocation *theInvocation = [NSInvocation invocationWithMethodSignature:theSignature];
    [theInvocation retainArguments];
    
    // Now for the real automagic fun!  We will loop through our arugment list,
    // and match up each parameter with the corresponding index value.
    
    // Since Objective-C actually converts messages into C methods:
    NSUInteger argumentCount = [theSignature numberOfArguments] - 2;
    
    [theInvocation setTarget:self];			// There's our index 0.
    [theInvocation setSelector:aSelector];	// There's our index 1.
    
    // Use the va_* macros to retrieve the arguments.
    va_list arguments;
    
    // Tell it where the optional args start.
    // Since the first parameter, the selector
    // Isn't optional, tell it to start with 'context'.
    va_start (arguments, objects);
    
    // Now for arguments 2+
    NSUInteger i, count = argumentCount;
    void* currentValue = objects;
    for (i = 0; i < count; i++)
    {
        // If we run out of arguments, then we pass nil
        // to the remaining parameters.
        [theInvocation setArgument:&currentValue atIndex:(i + 2)]; // The +2 represents self and cmd offsets
        currentValue = va_arg(arguments, void*); // Read the next argument in the list.
        
        // We should also handle the case where we have
        // *more* arguments than parameters.  This will
        // let us cover cases where we are invoking
        // other variadic methods (like arrayWithObjects:).
    }
    
    // Dispose of our C byte-array.
    va_end (arguments);
    
    // Invoke on our thread.
    [theInvocation performSelector:@selector(invoke) onThread:aThread withObject:nil waitUntilDone:wait];
    
    // Our NSInvocation is already autoreleased, so we're done.
}
@end
